////////////////////////////////////////////////////////////////////////////////
//
//  Alexey Kolonitsky
//  Kolonitsky Developers Aliance (c) 2011
//
////////////////////////////////////////////////////////////////////////////////

package gallery
{
    import flash.display.Sprite;
    import flash.display.DisplayObject;
    import flash.events.TimerEvent;
    import flash.events.Event;
    import flash.utils.Timer;
    
    import caurina.transitions.Tweener;

    /**
     *  Swithc DisplayObject by timer.
     *
     *  All dispaly object before shown registred by <code>registerSlide</code>
     *  function.
     *
     *  ImageSlideShow has follow methods for control palyback.
     *  <ul>
     *    <li>play &#151; play slide show</li>
     *    <li>stop &#151; stop slide show and reset timer to default value</li>
     *    <li>pause &#151; stop slide show and save last showed slide</li>
     *    <li>goto &#151; show specific slide by it index</li>
     *    <li>next &#151; show next slide</li>
     *    <li>prev $#151; show previous slide</li>
     *  </ul>
     */
    public class ImageSlideShow extends Sprite
    {
        /**
         *  Default delay between slides.
         */
        public static const DEFAULT_DELAY:Number = 15000;


        //----------------------------------------------------------------------
        //
        //  Properties
        //
        //----------------------------------------------------------------------

        //------------------------------
        // index
        //------------------------------

        private var _index:int = 1;

        /**
         *  Current slide index
         */
        public function get index():int
        {
            return _index;
        }


        //------------------------------
        // isPlayed
        //------------------------------

        private var _isPlayed:Boolean = false;

        /**
         *  Current slide index
         */
        public function get isPlayed():Boolean
        {
            return _isPlayed;
        }


        //------------------------------
        // total
        //------------------------------

        /**
         *  Total count of slides
         */
        public function get total():int
        {
            return slides.length;
        }


        //------------------------------
        // delay
        //------------------------------

        /**
         *  Time delay bitween two slides.
         */
        public function get delay():Number
        {
            return timer.delay;
        }

        public function set delay(value:Number):void
        {
            timer.delay = value;
        }



        //----------------------------------------------------------------------
        //
        //  Methods
        //
        //----------------------------------------------------------------------

        //------------------------------
        //  Constructor
        //------------------------------

        /**
         *  Create new slide show.
         *
         *  @param playOnStart
         *  @param delay
         */
        public function ImageSlideShow (playOnStart:Boolean = false,
            delay:Number = DEFAULT_DELAY)
        {
            timer = new Timer(delay);
            timer.addEventListener(TimerEvent.TIMER, timer_timerHandler);
            if (playOnStart)
                play();
        }


        /**
         *  Register slide in slide show. This method must be used for register
         *  all slides which must be showed in slide show. This method generate
         *  error if you try to regster slide in existing index.
         *
         *  @param index
         *  @param slide
         */
        public function registerSlide(index:int, slide:DisplayObject):void
        {
            trace("INFO: registerSlide(" + index + ", " + _newIndex + ")");

            slides[index] = slide;
            if (index == _newIndex)
                showNewSlide();
        }

        /**
         *  Remove slide at index if slide exist. If slide not exist method do
         *  nothing.
         *
         *  @param index specify slide which must be deleted.
         */
        public function removeSlide(index:int):void
        {
            //TODO: Clear position in slideshow
        }

        /**
         *
         *
         */
        public function hasSlide(index:int):Boolean
        {
            //TODO: 
            return false;
        }


        //----------------------------------------------------------------------
        //  Change slide manualy
        //----------------------------------------------------------------------


        /**
         *  Manualy switch to apropriate slide;
         *
         *  @param newIndex is a index of slide to show
         *          
         *  @return error code. Zero if evething ok
         * 
         */
        public function goto(newIndex:int):int
        {
            if (_newIndex == newIndex)
                return 0;

            _newIndex = newIndex;

            if (_newIndex in slides && slides[_newIndex])
            {
                showNewSlide();
            }
            else
            {
                trace("WARNING: Slide " + _newIndex + " not registred");
                return 1;
            }
            return 0;
        }

        /**
         *  Show next slide
         *
         *  @return error code. Zero if evething ok
         */
        public function next():int
        {
            return goto(index + 1);
        }

        /**
         *  Show prevoius slide
         *
         *  @return error code. Zero if evething ok
         */
        public function prev():int
        {
            return goto(index - 1)
        }


        //----------------------------------------------------------------------
        //  Slideshow playback
        //----------------------------------------------------------------------

        public function play():void
        {
            _isPlayed = true;
            timer.start();
        }

        public function pause():void
        {
            _isPlayed = false;
            timer.stop();
        }

        public function stop():void
        {
            _isPlayed = false;
            timer.stop();
            timer.reset();
        }




        //----------------------------------------------------------------------
        //
        //  Private logic
        //
        //----------------------------------------------------------------------

        private var timer:Timer = null;

        /**
         *  Index of current slide not showed. Is next
         *  iteration of timer
         */
        private var _newIndex:int = 1;

        private var slides:Array /* of DispalyObject */ = [];

        private function showNewSlide ():void
        {
            var target:DisplayObject = slides[_newIndex];
            
            trace("INFO: showNewSlide, showIndex = " + _newIndex
                + ", hideIndex = " + index);
            
            hide(slides[index]);
            show(target, slide_showCallback);
        }


        private function show(target:DisplayObject, callback:Function=null):void
        {
            //TODO: EFFECT file
            target.alpha = 0.0;
            fitImage(target, stage.stageWidth, stage.stageHeight);
            Tweener.addTween(target, {alpha:1.0, time:10, onComplete:callback, transition:"linear"});
            if (target.y < -50)
                Tweener.addTween(target, {y:0.0, time:10 + DEFAULT_DELAY / 1000, transition:"linear"});
            addChild(target);
        }

        private function hide(target:DisplayObject, callback:Function=null):void
        {
            //TODO: EFFECT file
            if (contains(target))
            {
                Tweener.addTween(target, {alpha:0.0, time:10, onComplete:onComplete, transition:"linear"});
            }

            function onComplete ():void
            {
                removeChild(target);
                if ( Boolean(callback) )
                    callback();
            }
        }

        private function fitImage(target:DisplayObject, fitWidth:Number,
            fitHeight:Number):void
        {
            if (target.scaleX == 1 || target.scaleY == 1)
            {
                var k:Number = fitWidth > fitHeight ? fitWidth / target.width : fitHeight / target.height;
                target.scaleX = k;
                target.scaleY = k;
            }

            target.x = (target.width - fitWidth) / 2;
            target.y = -(target.height - fitHeight) / 2;
        }


        //----------------------------------------------------------------------
        //  Event handlers
        //----------------------------------------------------------------------

        private function timer_timerHandler(event:TimerEvent):void
        {
            trace("INFO:TIMER _newIndex = " + _newIndex + ", total = " + total);
            
            if (_newIndex == total - 1)
            {
                trace("INFO: Slide show is complete. Go to first slide.");
                timer.reset();

                goto(1);
                
                return;
            }
            else
            {
                var i:int = next();
            }

            // _newIndex setted in goto function if stop timer if requaired
            // slide not registred.
            //
            // There timer stop, but not changed isPlayed flag. And slide show
            // resume when requaired slide will be registred.
            //
            if (i != 0)
                timer.stop();
        }

        private function slide_showCallback():void
        {
            _index = _newIndex;
            // when new slide is show playback resored if it play
            if (isPlayed && !timer.running)
                timer.start();
        }
    }
}
